package cn.iocoder.yudao.module.infra.service.job;

import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
import cn.iocoder.yudao.framework.quartz.core.util.CronUtils;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobSaveReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobMapper;
import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.ym.dal.dataobject.area.AreaDO;
import cn.iocoder.yudao.module.ym.dal.dataobject.site.SiteDO;
import cn.iocoder.yudao.module.ym.dal.mysql.area.AreaMapper;
import cn.iocoder.yudao.module.ym.dal.mysql.site.SiteMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.containsAny;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;

/**
 * 定时任务 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class JobServiceImpl implements JobService {

    @Resource
    private JobMapper jobMapper;

    @Resource
    private AreaMapper areaMapper;

    @Resource
    private SiteMapper siteMapper;
    @Resource
    private AdminUserMapper adminUserMapper;

    @Resource
    private SchedulerManager schedulerManager;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createJob(JobSaveReqVO createReqVO) throws SchedulerException {
        Integer type = createReqVO.getType();
        validateCronExpression(createReqVO.getCronExpression());
        // 1.1 校验唯一性
       /* if (jobMapper.selectByHandlerName(createReqVO.getHandlerName()) != null) {
            throw exception(JOB_HANDLER_EXISTS);
        }*/
        if (jobMapper.selectByJobName(createReqVO.getName()) != null) {
            throw exception(JOB_NAME_EXISTS);
        }
        // 1.2 校验 JobHandler 是否存在
        validateJobHandlerExists(createReqVO.getHandlerName());

        // 2. 插入 JobDO
        JobDO job = BeanUtils.toBean(createReqVO, JobDO.class);
        job.setStatus(JobStatusEnum.INIT.getStatus());
        fillJobMonitorTimeoutEmpty(job);
        jobMapper.insert(job);
        if(type != null && type ==2){
            job.setHandlerParam(job.getId().toString());
        }

        // 3.1 添加 Job 到 Quartz 中
        schedulerManager.addJob(job.getId(),job.getName(), job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
                createReqVO.getRetryCount(), createReqVO.getRetryInterval());
        // 3.2 更新 JobDO
        JobDO updateObj = JobDO.builder().id(job.getId()).status(JobStatusEnum.NORMAL.getStatus()).build();
        if(type != null && type ==2){
            updateObj.setHandlerParam(job.getId().toString());
        }
        jobMapper.updateById(updateObj);
        return job.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateJob(JobSaveReqVO updateReqVO) throws SchedulerException {
        validateCronExpression(updateReqVO.getCronExpression());
        // 1.1 校验存在
        JobDO job = validateJobExists(updateReqVO.getId());
        // 1.2 只有开启状态，才可以修改.原因是，如果出暂停状态，修改 Quartz Job 时，会导致任务又开始执行
        if (!job.getStatus().equals(JobStatusEnum.NORMAL.getStatus())) {
            throw exception(JOB_UPDATE_ONLY_NORMAL_STATUS);
        }
        // 1.3 校验 JobHandler 是否存在
        validateJobHandlerExists(updateReqVO.getHandlerName());

        // 2. 更新 JobDO
        JobDO updateObj = BeanUtils.toBean(updateReqVO, JobDO.class);
        JobDO jobDO = jobMapper.selectOne(new LambdaQueryWrapper<JobDO>().eq(JobDO::getName,updateReqVO.getName()));
        if(jobDO != null && !Objects.equals(jobDO.getId(), updateObj.getId())){
            throw exception(JOB_NAME_EXISTS);
        }

        fillJobMonitorTimeoutEmpty(updateObj);
        jobMapper.updateById(updateObj);

        // 3. 更新 Job 到 Quartz 中
        schedulerManager.updateJob(job.getHandlerName(),job.getName(), updateReqVO.getHandlerParam(), updateReqVO.getCronExpression(),
                updateReqVO.getRetryCount(), updateReqVO.getRetryInterval());
    }

    private void validateJobHandlerExists(String handlerName) {
        try {
            Object handler = SpringUtil.getBean(handlerName);
            assert handler != null;
            if (!(handler instanceof JobHandler)) {
                throw exception(JOB_HANDLER_BEAN_TYPE_ERROR);
            }
        } catch (NoSuchBeanDefinitionException e) {
            throw exception(JOB_HANDLER_BEAN_NOT_EXISTS);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateJobStatus(Long id, Integer status) throws SchedulerException {
        // 校验 status
        if (!containsAny(status, JobStatusEnum.NORMAL.getStatus(), JobStatusEnum.STOP.getStatus())) {
            throw exception(JOB_CHANGE_STATUS_INVALID);
        }
        // 校验存在
        JobDO job = validateJobExists(id);
        // 校验是否已经为当前状态
        if (job.getStatus().equals(status)) {
            throw exception(JOB_CHANGE_STATUS_EQUALS);
        }
        // 更新 Job 状态
        JobDO updateObj = JobDO.builder().id(id).status(status).build();
        jobMapper.updateById(updateObj);

        // 更新状态 Job 到 Quartz 中
        if (JobStatusEnum.NORMAL.getStatus().equals(status)) { // 开启
            schedulerManager.resumeJob(job.getName());
        } else { // 暂停
            schedulerManager.pauseJob(job.getName());
        }
    }

    @Override
    public void triggerJob(Long id) throws SchedulerException {
        // 校验存在
        JobDO job = validateJobExists(id);

        // 触发 Quartz 中的 Job
        schedulerManager.triggerJob(job.getId(),job.getName(), job.getHandlerName(),job.getHandlerParam());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncJob() throws SchedulerException {
        // 1. 查询 Job 配置
        List<JobDO> jobList = jobMapper.selectList();

        // 2. 遍历处理
        for (JobDO job : jobList) {
            // 2.1 先删除，再创建
            schedulerManager.deleteJob(job.getName());
            schedulerManager.addJob(job.getId(), job.getName(),job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
                    job.getRetryCount(), job.getRetryInterval());
            // 2.2 如果 status 为暂停，则需要暂停
            if (Objects.equals(job.getStatus(), JobStatusEnum.STOP.getStatus())) {
                schedulerManager.pauseJob(job.getName());
            }
            log.info("[syncJob][id({}) handlerName({}) 同步完成]", job.getId(), job.getHandlerName());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteJob(Long id) throws SchedulerException {
        // 校验存在
        JobDO job = validateJobExists(id);
        // 更新
        jobMapper.deleteById(id);

        // 删除 Job 到 Quartz 中
        schedulerManager.deleteJob(job.getName());
    }

    private JobDO validateJobExists(Long id) {
        JobDO job = jobMapper.selectById(id);
        if (job == null) {
            throw exception(JOB_NOT_EXISTS);
        }
        return job;
    }

    private void validateCronExpression(String cronExpression) {
        if (!CronUtils.isValid(cronExpression)) {
            throw exception(JOB_CRON_EXPRESSION_VALID);
        }
    }

    @Override
    public JobDO getJob(Long id) {
        return jobMapper.selectById(id);
    }

    @Override
    public PageResult<JobDO> getJobPage(JobPageReqVO pageReqVO) {
        PageResult<JobDO> jobDOPageResult = jobMapper.selectPage(pageReqVO);
        List<JobDO> list = jobDOPageResult.getList();
        if(list!= null && !list.isEmpty()){

                List<AreaDO> areaDOList = areaMapper.selectList();
                // 正确转换方式
                Map<Long, String> areaMap = Optional.ofNullable(areaDOList)
                        .orElseGet(Collections::emptyList) // 处理null列表
                        .stream()
                        .collect(Collectors.toMap(
                                AreaDO::getId,  // Key提取器
                                AreaDO::getAreaName,  // Value提取器（对象本身）
                                (oldValue, newValue) -> oldValue  // 处理重复键：保留第一个值
                        ));
                List<AdminUserDO> adminUserDOS = adminUserMapper.selectList();
                // 正确转换方式
                Map<Long, String> userMap = Optional.ofNullable(adminUserDOS)
                        .orElseGet(Collections::emptyList) // 处理null列表
                        .stream()
                        .collect(Collectors.toMap(
                                AdminUserDO::getId,  // Key提取器
                                AdminUserDO::getNickname,  // Value提取器（对象本身）
                                (oldValue, newValue) -> oldValue  // 处理重复键：保留第一个值
                        ));
                // 正确转换方式
                List<SiteDO> siteDOList = siteMapper.selectList();
                Map<Long, String> siteMap = Optional.ofNullable(siteDOList)
                        .orElseGet(Collections::emptyList) // 处理null列表
                        .stream()
                        .collect(Collectors.toMap(
                                SiteDO::getId,  // Key提取器
                                SiteDO::getSiteName,  // Value提取器（对象本身）
                                (oldValue, newValue) -> oldValue  // 处理重复键：保留第一个值
                        ));


                for (JobDO jobDO : list) {
                    if(jobDO.getType() == 2){
                        String areaName = areaMap.get(jobDO.getAreaId());
                        if(areaName != null && !areaName.isEmpty()){
                            jobDO.setAreaName(areaName);
                        }
                        String userName = userMap.get(jobDO.getOperatorId());
                        if(userName != null && !userName.isEmpty()){
                            jobDO.setOperatorName(userName);
                        }
                        String result = Arrays.stream(jobDO.getSiteIds().split(","))
                                .map(Long::parseLong)       // 转换为 Long 类型
                                .map(id -> siteMap.get(id))     // 从 Map 中获取对应值
                                .filter(Objects::nonNull)   // 过滤掉不存在的键
                                .collect(Collectors.joining(",")); // 拼接结果
                        jobDO.setSiteNames(result);
                    }
                }
        }
        return jobDOPageResult;
    }

    private static void fillJobMonitorTimeoutEmpty(JobDO job) {
        if (job.getMonitorTimeout() == null) {
            job.setMonitorTimeout(0);
        }
    }

}
